#include "std/debug.h"
#include "arch/hw_kb.h"
#include "kernel/irq.h"
#include "kernel/proc.h"

#define raw_data_buf_size		(32)
static unsigned char kb_raw_data_ring_buf[raw_data_buf_size] = {0};

// global input buffer list
static listnode_t kb_input_buffer_list = LISTNODE_INITIALIZER(kb_input_buffer_list);

// keyboard handler spinlock
DECLARE_PRIVATE_SPINLOCK(kb_spinlock);

static unsigned int kb_raw_data_head = 0;
static unsigned int kb_raw_data_tail = 0;

static int kb_add_raw_data(unsigned int rawdata)
{
	if (kb_raw_data_head == kb_raw_data_tail + 1)
		return 0;
	kb_raw_data_ring_buf[kb_raw_data_tail] = (unsigned char)rawdata;
	kb_raw_data_tail = (kb_raw_data_tail + 1) % raw_data_buf_size;
	return 1;
}

static int kb_get_raw_data(unsigned int* val)
{
	if (kb_raw_data_head == kb_raw_data_tail)
		return 0;
	*val = kb_raw_data_ring_buf[kb_raw_data_head];
	kb_raw_data_head = (kb_raw_data_head + 1) % raw_data_buf_size;
	return 1;
}

static int kb_input_buffer_add(kb_input_buffer_t* buf, unsigned int code)
{
	kassert(NULL != buf);
	if (kb_input_buffer_is_empty(buf))
		return 1;

	buf->buffer[buf->tail] = code;
	buf->tail = (buf->tail + 1) % buf->buf_size;
	return 0;
}

static kb_input_buffer_t* kb_create_input_buffer(void)
{
	kb_input_buffer_t* buf = (kb_input_buffer_t*)kmalloc(sizeof(kb_input_buffer_t));
	if (NULL == buf) return NULL;

	buf->buf_size = kb_input_buf_max_sz;
	buf->head = buf->tail = 0;
	buf->buffer = (unsigned int*)kmalloc(kb_input_buf_max_sz * sizeof(unsigned int));
	if (NULL == buf->buffer)
	{
		kfree(buf);
		buf = NULL;
	}
	return buf;
}

static void kb_notify(unsigned int code)
{
	listnode_t* node;

	if (listnode_isempty(kb_input_buffer_list))
		return;

	spin_lock(&kb_spinlock);
	for (node = kb_input_buffer_list.next; node != &kb_input_buffer_list; node = node->next)
	{
		kb_input_buffer_t *ibuf = list_entry(kb_input_buffer_t, ownerlist, node);
		kb_input_buffer_add(ibuf, code);
	}
	spin_unlock(&kb_spinlock);
}

static void keyboard_tasklet_handler(uint d)
{
	unsigned int raw_data;
	while (kb_get_raw_data(&raw_data))
	{
		unsigned int code = kb_translate_key(raw_data);
		if (!code) continue;

		kb_update_leds(code);
		kb_notify(code);

		if (code & 0xFF)
			printk("%c", code & 0xFF);
		else printk("(%04x)", (code >> 8) & 0xFFFF);
	}
}

// tasklet structure
static tasklet_t kb_tasklet =
{
	NULL,
	0, 0,
	keyboard_tasklet_handler,
	0
};

static void keyboard_handler(int irq, void* devid, regs_t* regs)
{
	kb_add_raw_data(hw_kb_int_read_key());
	tasklet_schedule(&kb_tasklet);
}

static irqaction_t kb_irqaction =
{
	keyboard_handler,
	IAF_NORELEASE | SA_SHIRQ,
	"keyboard",
	(void*)DEVID_KEYBOARD,
	NULL,
};

void global_init_keyboard_module(void)
{
	int ret = _request_irq(1, &kb_irqaction);
	kassert(!ret);
}

/* EOF */
